import warnings
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from sklearn.linear_model.coordinate_descent import ConvergenceWarning
from sklearn.linear_model import LogisticRegressionCV
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# 设置字符集，防止中文乱码
mpl.rcParams['font.sans-serif']=[u'simHei']
mpl.rcParams['axes.unicode_minus']=False

# 拦截异常
warnings.filterwarnings(action='ignore',category=ConvergenceWarning)

columns=['id','Clump Thickness','Uniformity of Cell Size','Uniformity of Cell Shape',
         'Marginal Adhesion','Single Epithelial Cell Size','Bare Nuclei',
         'Bland Chromatin','Normal Nucleoli','Mitoses','Class']

def run():
    # 数据读取并处理异常数据
    path = '../data/breast-cancer-wisconsin.data'
    df = pd.read_csv(path, header=None, names=columns)
    datas = df.replace('?', np.nan).dropna(how='any')

    ## 数据读取并处理异常数据
    ## 提取
    X=datas[columns[1:10]]
    Y=datas[columns[10]]

    ## 分隔
    X_train,X_test,Y_train,Y_test=train_test_split(X,Y,test_size=0.1,random_state=0)

    # 2. 数据格式化(归一化)
    ss=StandardScaler()
    X_train=ss.fit_transform(X_train)# 训练模型及归一化数据

    # 3. 模型构建及训练
    ## penalty: 过拟合解决参数,l1或者l2
    ## solver: 参数优化方式
    ### 当penalty为l1的时候，参数只能是：liblinear(坐标轴下降法)；
    ### nlbfgs和cg都是关于目标函数的二阶泰勒展开
    ### 当penalty为l2的时候，参数可以是：lbfgs(拟牛顿法)、newton-cg(牛顿法变种)，seg(minibatch)
    # 维度<10000时，lbfgs法比较好，   维度>10000时， cg法比较好，显卡计算的时候，lbfgs和cg都比seg快
    ## multi_class: 分类方式参数；参数可选: ovr(默认)、multinomial；这两种方式在二元分类问题中，效果是一样的；在多元分类问题中，效果不一样
    ### ovr: one-vs-rest， 对于多元分类的问题，先将其看做二元分类，分类完成后，再迭代对其中一类继续进行二元分类
    ### multinomial: many-vs-many（MVM）,对于多元分类问题，如果模型有T类，我们每次在所有的T类样本里面选择两类样本出来，
    #### 不妨记为T1类和T2类，把所有的输出为T1和T2的样本放在一起，把T1作为正例，T2作为负例，
    #### 进行二元逻辑回归，得到模型参数。我们一共需要T(T-1)/2次分类
    ## class_weight: 特征权重参数

    ### TODO: Logistic回归是一种分类算法，不能应用于回归中(也即是说对于传入模型的y值来讲，不能是float类型，必须是int类型)
    lr=LogisticRegressionCV(multi_class='ovr',fit_intercept=True,Cs=np.logspace(-2,2,20),cv=2,penalty='l2',solver='lbfgs',tol=0.01)
    re=lr.fit(X_train,Y_train)

    # 4. 模型效果获取
    r = re.score(X_train, Y_train)
    print("R值（准确率）：", r)
    print("稀疏化特征比率：%.2f%%" % (np.mean(lr.coef_.ravel() == 0) * 100))
    print("参数：", re.coef_)
    print("截距：", re.intercept_)
    print(re.predict_proba(X_test))

    # 数据预测
    ## a. 预测数据格式化(归一化)
    X_test=ss.transform(X_test)# 使用模型进行归一化操作
    ## b. 结果数据预测
    Y_predict=lr.predict(X_test)

    ## c. 图表展示
    x=range(len(X_test))
    plt.figure(figsize=(14,7),facecolor='w')
    plt.ylim(0,6)
    plt.plot(x,Y_test,'ro',markersize=8,zorder=3,label=u'真实值')
    plt.plot(x,Y_predict,'go',markersize=14,zorder=2,label=u'预测值,R^2=%.3f' % re.score(X_test,Y_test))
    plt.legend(loc='upper left')
    plt.xlabel(u'数据编号',fontsize=18)
    plt.ylabel(u'乳腺癌类型',fontsize=18)
    plt.title(u'Logistic回归算法对数据进行分类',fontsize=20)
    plt.show()


run()
